//SimplePeer = require("./simplepeer.min.js");
//import EventEmitter from "wolfy87-eventemitter";
//EventEmitter = require("wolfy87-eventemitter");

//const { WebNetworkState, WebNetworkType } = require("./networkStatus.js");

//import { IpfsGroup } from "./ipfs-group";
//import { Renderzvous } from "./rendezvous";

// / for push response
const PUSH_RESP_OK = 0;             // ok or repeat
const PUSH_RESP_LAYER_LOW = 1;
const PUSH_RESP_LOOP = 2;           // maybe loop
const PUSH_RESP_NO_RESOURCE = 3;

// state graph:
// i join with other peer(will use as candidate) info: PEER_STATE_TEMP
// candidate connected: PEER_STATE_WORKING 
const PEER_STATE_TEMP = 0;
const PEER_STATE_WAITING_AS_CANDIDATE = 1;
const PEER_STATE_WAITING_AS_RECEIVER = 2;
const PEER_STATE_WORKING = 5;               // normal working


const PEER_ROLE = {
    NON             : 0,
    PUSHER          : 2,
    PUSHER_BACKUP   : 3,
    PARTNER         : 4,
    CANDIDATE       : 5,
    RECEIVER        : 6,
    TEMP            : 7
}


// 如果发送者关闭了，需要定时检查数据接收情况，适时更换
// 如果接收者关闭了，如果感知？（接收者直接关页面，目前感知不到）
// 如果接收到不需要的数据，如何处理（通知对方别发了）
// 由于是异步的，要考虑时序问题

class PeerNode {
    // isInitiator: 是否是连接发起方（主动提交offer）
    constructor(hostId, peerId, layerNo, isInitiator, callbacks) {
        this.hostId_ = hostId;
        this.peerId_ = peerId;
        this.layerNo_ = layerNo;
        this.isInitiator_ = isInitiator;
        this.dataChannelIsOk_ = false;
        this.state = PEER_STATE_TEMP;       // 处在临时节点时的状态
        this.callbacks_ = callbacks;

        this.simplePeer_ = new SimplePeer({ initiator: isInitiator });
        this.simplePeer_.on("signal", data => {
            console.log("send signal");

            // send signal to this peer by server(sock.io)
            this.callbacks_.onSignal(this.peerId_, data);
            // peer2.signal(data)
        });

        this.simplePeer_.on("connect", () => {
            this.dataChannelIsOk_ = true;
            this.callbacks_.onConnect && this.callbacks_.onConnect(this);    
            
            let isUsingTurn = this.usingTurn()
            console.log("using turn: ", isUsingTurn)
        });
        this.simplePeer_.on("error", (err) => {
            this.dataChannelIsOk_ = false;
            this.callbacks_.onError && this.callbacks_.onError(this, err);
        });
        this.simplePeer_.on("data", data => {
            // 消息和数据都在这里发了； 最好只发数据;  目前做
            try {
                if (data[0] == 123) {
                    var val = JSON.parse(data)
                    if (val.type && val.type != "data") {
                        this.callbacks_.onMsg && this.callbacks_.onMsg(this, val);                
                        return
                    }
                }
            } catch (e) {
                console.log(e)
            }
            this.callbacks_.onData && this.callbacks_.onData(this, data);
        });
        
        this.simplePeer_.on("close", () => {
            this.callbacks_.onClose && this.callbacks_.onClose(this);
        });
    }
    isInitiator() { 
        return this.isInitiator_;
    }
    getPeerId() {
        return this.peerId_;
    }
    getLayerNo() {
        return this.layerNo_;
    }
    getInfo() {
        return {peerId:this.peerId_, layerNo:this.layerNo_}
    }
    setLayerNo(layerno) {
        this.layerNo_ = layerno;
    }

    signal(data) {
        console.log("received signal");
        this.simplePeer_.signal(data);
    }

    // / relied send mode; message 都认为时候json对象
    sendMessage(msg) {
        try {
            this.simplePeer_.send(JSON.stringify(msg));
        } catch (err) {
            this.callbacks_.onClose && this.callbacks_.onClose(this);
            console.log("send message error: ", err);
        }
    }
    // / unrelied send mode
    sendData(data) {
        try {
            this.simplePeer_.send(data);
        } catch (err) {
            this.callbacks_.onClose && this.callbacks_.onClose(this);
            console.log("send data error: ", err);
        }
    }

    usingTurn() {
/*        pc = this.simplePeer_.getPC()
        const stats = await pc.getStats()
        let selectedLocalCandidate
        for (const {type, state, localCandidateId} of stats.values())
            if (type === 'candidate-pair' && state === 'succeeded' && localCandidateId) {
                selectedLocalCandidate = localCandidateId
                break
            }
        return !!selectedLocalCandidate && stats.get(selectedLocalCandidate)?.candidateType === 'relay'
  */      
        var isUsingTurn = false;        
        this.simplePeer_.getStats((err, items) => {
            if (err) items = []
            
            //let selectedLocalCandidate
            items.forEach(item => {
                if (item.type === 'candidate-pair' && item.state === 'succeeded' && item.localCandidateId) {
              //      selectedLocalCandidate = item.localCandidateId
                    isUsingTurn = (item.candidateType === 'relay')
                    return
                }
            })
            //return !!selectedLocalCandidate && stats.get(selectedLocalCandidate)?.candidateType === 'relay'
        })

        return isUsingTurn;  
    }
}



const MAX_PARTNER_NUM = 5;
const MAX_CANDIDATE_NUM = 8;
const MAX_RECEIVER_NUM = 4;     // 下推数据的数量，还应该考虑自己的实际能力

/**
 * provide alm transform
 * @desc 节点可以向上级或者同级节点要数据
 * interface:
 *      create  create this alm
 *      join    join this alm
 *      quit    stop this alm
 *      send    send data by alm
 * event:
 *      'create', ret
 *      'join', ret
 *      'error', err
 *      'data', blob
 */
export class Lalm extends EventEmitter {
    // 要求socket类似io.socket, 目前只需要提供emit和on两个函数; 
    // 未来，socket可以为null，内部将使用类似ipfs的技术完成信令交互
    constructor(socket, almId, opts = {}) {
        super();

        this.opts_ = opts;
        /* opts: {
            maxConns: Number, // Max number of connections
            nodeId: String|Buffer, // DHT protocol node ID (default=randomly generated)
            peerId: String|Buffer, // Wire protocol peer ID (default=randomly generated) 
            dht: Boolean|Object, // Enable DHT (default=true), or options object for DHT       
            }
        */
        this.almId_ = almId;
                
        this.statics = {
            downloaded: 0,
            uploaded: 0
        };

        // sender' node id
        this.rootId_;
        if (typeof opts.rootId === "string") {
            this.rootId_ = opts.rootId;
        }

        this.max_receiver_num_ = opts.max_receiver_num || MAX_RECEIVER_NUM;

        // Peer's node ID
        this.selfPeerId_;
        if (typeof opts.selfId === "string") {
            this.selfPeerId_ = opts.selfId;
        } /* else if (Buffer.isBuffer(opts.peerId)) {
            this.selfPeeId_ = opts.peerId.toString('hex')
            } else {
            this.selfPeeId_ = Buffer.from(VERSION_PREFIX + randombytes(9).toString('base64')).toString('hex')
            } */

        // / 数据源：下面的互不交叉
        // 
        this.pusher_;       // = new PeerNode();    // 当前最多只会有一个pusher
        this.backupPusher_; // = new PeerNode();    // 后备，随时可以用于替换pusher
        this.partners_ = new Map(); // id->PeerNode; active and webrtc datachannel is ok;  // 用于寻找backupPusher
        // this.hisPeers = new Set();       // lrumap
        this.pusher_candidates_ = new Map();// peerNodes, all peer get from server or partners;
        this.temp_peers_ = new Map();       // 临时节点（通过state表示其目的），目前只有来拉数据的节点; 后续也可以是邻居节点（想获取更多的数据节点）
        // 层数作为参考，不要求严格，实际上还是一种传输质量驱动的模型
        // 当前层号（源节点为0层）就是自己正在拉流节点的层号+1
        // 层号会跟着当前拉流节点动态变化，不要求严格一致，所以变动时不通知其他人
        // 处理流程：
        /* 启动流程： 
            1. 首先从源或者RP获取初始的上游节点；
            2. 试图和这些节点建立连接；
            3. 比较连接的质量，设置pusher，back_pusher, candidate     第一步，先简单点，先不比较质量了       
        */
       /* 不断和pusher，back_pusher交换candidate信息
       */
        this.receivers_ = new Map();    // 数据的接收者 PeerNode
      

        this.socket_ = socket;
        // 建立 simple peer 连接； 信号可能是对方发出来的（offer/answer）
        this.socket_.on('signal', (from, data) => {
            let peer = this._findPeerNode(from);
            if (!peer) {
                console.log("Received signal from RP, create peer node for not find user: ", from);

                // / if receive offer ...
                peer = new PeerNode(
                    this.selfPeerId_,
                    from,
                    -1,
                    false,
                    this.peerCallbacks_
                );                
                this.temp_peers_.set(from, peer);
                console.log('find this peer:', this.temp_peers_.has(from));
                console.log('onSignal,candidate size： ', this.temp_peers_.size);
            }
            peer.signal(data);
        });

        this.peerCallbacks_ = {
            onSignal: this._onPeerSignal.bind(this),        // 专为simple_peer建立连接，转发信令使用
            onData: this._onPeerReceivedData.bind(this),
            onMsg: this._onPeerReceivedMessage.bind(this),
            onConnect: this._onPeerConnect.bind(this),
            onError: this._onPeerError.bind(this),
            onClose: this._onPeerClose.bind(this)            
        };

        this.lastSeq_ = 0; // 收到的数据包最后的一个编号，  以后考虑中间补包的情况， 如果是0表示下发者可以从当前收包编号开始

        // / cache data
        this.datas_ = new Map(); // / seq: data

        const SELF_CHECK_TIME = 500;            // ms
        this.checkTimerId_ = setInterval(() => this._onTimeCheck(), SELF_CHECK_TIME);
        const MEMBERS_EXCHANGE_TIME = 10000;    // ms
        this.memberTimerId_ = setInterval(() => this._onMemberExchange(), MEMBERS_EXCHANGE_TIME);
    }
    deconstructor() {
        clearInterval(this.checkTimerId_);
        clearInterval(this.memberTimerId_);
    }

    async init() {
//        this.groupChannel = new IpfsGroup(this.almId_, this.selfPeerId_, this.groupCallbacks_);
//        await this.groupChannel.init();
    }

    isRoot() {
        return this.selfPeerId_ == this.rootId_;
    }

    _isPeerExisted(peer) {
        return (
            (this.pusher_ && peer == this.pusher_.getPeerId()) ||
            (this.backupPusher_ && peer == this.backupPusher_.getPeerId()) ||
            this.partners_.has(peer) ||
            this.pusher_candidates_.has(peer) ||
            this.temp_peers_.has(peer) ||
            this.receivers_.has(peer)
        );
    }
    _findPeerNode(peerId) {
        if (this.pusher_ && peerId == this.pusher_.getPeerId())
            return this.pusher_;
        if (this.backupPusher_ && peerId == this.backupPusher_.getPeerId()) {
            return this.backupPusher_;
        }
        if (this.partners_.has(peerId)) return this.partners_.get(peerId);
        if (this.pusher_candidates_.has(peerId)) return this.pusher_candidates_.get(peerId);
        if (this.temp_peers_.has(peerId)) return this.temp_peers_.get(peerId);
        if (this.receivers_.has(peerId)) return this.receivers_.get(peerId);
    }

    // 层次的模糊计算
    layerNo() {
        return this.pusher_ ? this.pusher_.getLayerNo() + 1 : -1;
    }
    
    // 作为数据源节点，需要调用create
    async create() {
	    this.rootId_ == this.selfPeerId_;
        this.sendBySocket('create', this.selfPeerId_, this.almId_);
      
        this.socket_.on('createResp', this._onCreate.bind(this));
    }
    _onCreate(ret) {
        console.log("received create response.");
        this.emit('create', ret);
    }

    // 作为拉流节点，调用join
    async join(rootId) {
 	this.rootId_ = rootId;
        if (this.isRoot()) {
            console.log('you are root already!')
            return;
        }
        this.sendBySocket('join', this.selfPeerId_, this.almId_);
        this.socket_.on('joinResp', this._onJoin.bind(this));
    }
    _onJoin(ret, layerNo, members) {
        console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_} joined, members: ${members.length}`);

        this.emit('join', ret);

        if (ret == 'success') {
           this._addMembers(members);
        } else {
            console.log("join ret: ", ret)
        }
    }

    _addMembers(members) {
        let candidate_num = this.pusher_candidates_.length
        for (const peerInfo of members) {  
            if (peerInfo.layerNo < 0) {
                console.log("error lay no:", peerInfo.layerNo);
                continue;
            }
            if (peerInfo.peerId == this.selfPeerId_) {
                console.log('Why self info!');
                continue;
            }
            
            if (candidate_num > MAX_CANDIDATE_NUM) {
                return;
            }

            if (!this._isPeerExisted(peerInfo.peerId)) {
                const peerNode = new PeerNode(
                    this.selfPeerId_,
                    peerInfo.peerId,
                    peerInfo.layerNo,
                    true,
                    this.peerCallbacks_
                );
                peerNode.state = PEER_STATE_WAITING_AS_CANDIDATE;
                this.temp_peers_.set(peerInfo.peerId, peerNode);
                
                candidate_num++;

                console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_} Add a candidate: `, peerInfo);
            } else {
                console.log("Peer is already existed: ", peerInfo);
            }
        }
    }

    getAlmId() {
        return this.almId_;
    }

    quit(callback) {
        console.log("i will quit");
        
        callback && callback();

        for (const peer of this.receivers_.values()) {
            peer.sendMessage({ type: "quit" });
        }

        // / tell rp(server)
        this.sendBySocket("quit");
    }

    _onTimeCheck() {
        this._updatePusher();
    }

    _send_members_info_to(peer, cmd_type) {
        let peerInfo = {peerId:this.selfPeerId_, layerNo:this.layerNo_}
        let members = [peerInfo];
        if (this.backupPusher_) {
            members.push(this.backupPusher_.getInfo())
        }
        for (const p of this.partners_.values()) {
            members.push(p.getInfo())
        }
      
        // peer
        peer.sendMessage({
            type: cmd_type,
            layer: this.layerNo(),
            members: members
        });
    //console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_} send member to receivers`);
    }
    _onMemberExchange() {
        // send member info to receiver
        for (const peer of this.receivers_.values()) {
            this._send_members_info_to(peer, 'members')
        }

        while (this.partners_.length > MAX_PARTNER_NUM) {
            this._movePeerTo(this.partners_.keys().next().value, PEER_ROLE.CANDIDATE)
        }

        // get member info from partner
        const NUM_4_MEMBER = 3;
        let count = 0;
        for (const peer of this.partners_.values()) {
            if (count++ >= NUM_4_MEMBER) {
                break;
            }

            peer.sendMessage({
                type: "members_req",
                layer: this.layerNo(),
            });
        }
    }

    _onPeerClose(peer) {
        console.log('close for user', peer.getPeerId());
        this._peerQuit(peer);
    }
    _onPeerError(peer, err) {
        console.log('error', err);        
        this._peerQuit(peer);
    }
    _onPeerConnect(peer) {
        const peerId = peer.getPeerId();

        if (peer.isInitiator()) {
//            peer.sendMessage({ type: "desc", info: {layerNo: this.layerNo()} });
        }
        
        if (!this.temp_peers_.has(peerId)) { 
            console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_} a peer: ${from} connected, but not in temp_peer`);
            return ;
        }
        
        console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_} connect with ${peerId}, candidate size: ${this.pusher_candidates_.size}`);
        
        // 拉流方式连上了可能的推流者
        if (peer.state == PEER_STATE_WAITING_AS_CANDIDATE) {
            console.assert(!this.isRoot(), "root node need not pull stream.");

            console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_} add candidate ${peerId}`);
            this._addACandidate(peer);
            peer.state = PEER_STATE_WORKING;
            this.temp_peers_.delete(peerId);
        }
        // PEER_STATE_WAITING_AS_RECEIVER, waiting for pushReq
    }

    _addACandidate(peer) {
        const peerId = peer.getPeerId();
        // 先安排好，后续根据响应情况来确定是否替换； 要有超时机制
        // 发现，viewer peer connection onopen后， 对方（例如broadcast）还没有onopen，先收到对方的pushreq，后onopen
        if (!this.pusher_) {
            // peer
            peer.sendMessage({
                type: "pushReq",
                layer: this.layerNo(),
                startSeq: this.lastSeq_
            });
            this.pusher_ = peer;
            console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_} set pusher: ${peerId}`);
        } else if (!this.backupPusher_) {
            this.backupPusher_ = peer;
        } else {
            if (peer.getLayerNo() <= this.layerNo()) {
                this.pusher_candidates_.set(peerId, peer);
            }
            console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_}. a peer with id: ${peerId} and layno is ${peer.getLayerNo()}, data channel is ok; my layno is ${this.layerNo()}`);
        }
    }

    // 需要通过外部通道（例如服务器）发送offer或者answer到peerId
    _onPeerSignal(peerId, data) {
        this.sendBySocket('signal', this.selfPeerId_, peerId, data);
    }
    
    _onPeerReceivedMessage(from, msg) {
        const fromId = from.getPeerId();
        console.log(`[lalm] at alm: ${this.almId_}, me: ${this.selfPeerId_}. Received peer message: ${msg.type} from: ${fromId}`);
        switch (msg.type) {
            case "quit":
                this._peerQuit(from);                
                break;
            case "stopPush":
                // move to temp peer
                this.temp_peers_.set(fromId, from);
                this.receivers_.delete(fromId);
                break;
            case 'members_req' :
                this._send_members_info_to(from, 'members_resp');
                break;
            case 'members_resp' :
            case 'members' :
                this._addMembers(msg.members);
                break;
            case "pushReq":
                // 只要不是自己的数据源，就可以给他推数据（层号根据源定）
                if ((this.pusher_ && from == this.pusher_) ||
                    (this.backupPusher_ && from == this.backupPusher_)) {
                    console.log("pusher can not be receiver: ", fromId);
                    from.sendMessage({
                        type: "pushResp",
                        code: PUSH_RESP_LOOP,
                        info: "Is pusher"
                    });
                } else if (this.receivers_.has(fromId)) {
                    // 都给数据
                    from.sendMessage({
                        type: "pushResp",
                        code: PUSH_RESP_OK
                    });
                } else {
                    console.log(`receive push req from user: ${fromId}, layno: ${msg.layer}.`);
                    from.setLayerNo(msg.layer);
                    
                    if (this.receivers_.size > this.max_receiver_num_) {
                        from.sendMessage({
                            type: "pushResp",
                            code: PUSH_RESP_NO_RESOURCE
                        });
                        // maybe in temp_peers
                        this.temp_peers_.delete(fromId);
                    } else {
                        from.sendMessage({
                            type: "pushResp",
                            code: PUSH_RESP_OK
                        });

                        /// move to receivers
                        this._movePeerTo(PEER_ROLE.RECEIVER, from);
                        
                        /// push data from msg.startSeq from cache
                        // send video key frame, ... todo
                    }
                }
                break;

            case "pushResp":
                console.log('receive push response, code: ', msg.code);
                switch (msg.code) {
                    case PUSH_RESP_OK:                        
                        // 注意好衔接，替换掉现有的pusher_
                        // 如果有pusher，不是自己，替换掉，需要注意顺利过渡
                        if (this.pusher_) {
                            if (this.pusher_ != from) {   // 用户进来时，有多个候选者时，会出现这种情况
                                // 暂时不替换，后续应该考虑为什么追加了当前push，是否需要替换掉
                                // 可以考虑先保留，等发现确实不要再stop
                                from.sendMessage({
                                    type: "stopPush"
                                });
                                this._movePeerTo(PEER_ROLE.PARTNER, from);
                                return;
                            }
                        }

                        // move to pusher_
                        this._movePeerTo(PEER_ROLE.PUSHER, from);
                        break;
                    case PUSH_RESP_LOOP:
                    case PUSH_RESP_LAYER_LOW:
                    case PUSH_RESP_NO_RESOURCE:
                        if (this.pusher_ && from == this.pusher_) {
                            this._movePeerTo(PEER_ROLE.CANDIDATE, from);
                            this.pusher_ = null;
                            this.partners_.set(fromId, this.pusher_);
                        } else if (this.backupPusher_ && from == this.backupPusher_) {
                            this._movePeerTo(PEER_ROLE.CANDIDATE, from);
                            this.partners_.set(fromId, this.backupPusher_);
                            this.backupPusher_ = null;
                        } else {
                            console.log(
                                "send push req at invalid peer ",
                                fromId
                            );
                        }

                        this._updatePusher();

                        break;
                }
                break;
            case "desc":
                from.setLayerNo(msg.info.layerNo);
                break;
            default:
        }
    }
    
    // only move user to the role
    _movePeerTo(role, peer) {
        console.assert(peer, "peer should no be null when move");

        const peerId = peer.getPeerId();
        console.log(`move peer ${peerId}  to ${role}`);

        if (this.pusher_ && peer == this.pusher_) {
            this.pusher_ = null;
        } else if (this.backupPusher_ && peer == this.backupPusher_) {                    
            this.backupPusher_ = null;
        } else if (this.partners_.has(peerId)) {
            this.partners_.delete(peerId);
        } else if (this.pusher_candidates_.has(peerId))
            this.pusher_candidates_.delete(peerId);
        else if (this.receivers_.has(peerId)) {
            this.receivers_.delete(peerId);
        } else {
            this.temp_peers_.delete(peerId);
        }
        
        switch (role) {
            case PEER_ROLE.PUSHER :
                this.pusher_ = peer;
                return;
            case PEER_ROLE.PUSHER_BACKUP :
                this.backupPusher_ = peer;
                return;
            case PEER_ROLE.PARTNER :
                this.partners_.set(peerId, peer);
                return;
            case PEER_ROLE.CANDIDATE :
                this.pusher_candidates_.set(peerId, peer);
                return;
            case PEER_ROLE.RECEIVER:
                this.receivers_.set(peerId, peer);
                return;
            case PEER_ROLE.TEMP :
                this.temp_peers_.set(peerId, peer);
                return;
            case PEER_ROLE.NON :
                //peer.destroy();
                return;
            default:
        }
    }

    _onPeerReceivedData(from, data) {
        // / chceck repeat;
        ///console.log(`Received data from ${from} with seq: `, blob.seq);
        //console.log(`${this.selfPeerId_} received data from: `, from.getPeerId());

        // / send to app; 
        this.emit("data", data);
        //var blob = new Blob([data], {type: "video/webm; codecs=opus,vp8"});               
        //this.emit("data", blob);

        // 以后考虑乱序的情况，需要根据buffer来确定。
        ///this.lastSeq_ = data.seq;

        this._relay(data);
    }    
    _relay(data)
    {
        // / send to receivers
        //console.log('relay data to num of receivers: ', this.receivers_.size);
        for (const peer of this.receivers_.values()) {
            peer.sendData(data);
        }
    }
    send(data) {        
        ///this.lastSeq_ = data.seq;
        ///console.log("Send data with seq: ", data.seq);
        //data.type = "data";    
     
        //console.log('send data, len: ', data.size);   
        
        this._relay(data);
        //this.fetchAB(data, (buf) => { this._relay(buf);  });        
    }

    fetchAB (file, cb) {
        let reader = new FileReader();
        reader.onload = function(e) {
            console.log(e.target.result);
            if(e.target.result.byteLength > 0) {        
                cb(new Uint8Array(e.target.result));
            }
        }
        reader.readAsArrayBuffer(file);
    }

    _peerQuit(peer) {        
        this._movePeerTo(PEER_ROLE.NON, peer);
        this._updatePusher();
    }

    _updatePusher() {
        if (!this.pusher_) {
            if (this.backupPusher_) {
                this.pusher_ = this.backupPusher_;
                this.backupPusher_ = null;
            }
            else if (this.partners_.length > 0) {
                // set the first partner as pusher
                const firstKey = this.partners_.keys().next().value;
                this.pusher_ = this.partners_.get(firstKey);
                this.partners_.delete(firstKey);
            }

            if (this.pusher_) {
                console.log('Select a new pusher.');
                
                this.pusher_.sendMessage({
                    type: "pushReq",
                    layer: this.layerNo_,
                    startSeq: this.lastSeq_
                });
            }
        }
        
        return;        
        if (!this.backupPusher_) {
            if (this.partners_.length > 0) {
                this.pusher_ = this.partners_;
                this.partners_.delete();
            }
            peer.sendMessage({
                type: "pushReq",
                layer: this.layerNo_,
                startSeq: this.lastSeq_
            });
        }
    }

    // Total download speed , in bytes/sec.
    downloadSpeed() {}
    // Total upload speed , in bytes/sec.
    uploadSpeed() {}
    downloaded() {
        return this.statics.downloaded;
    }
    uploaded() {
        return this.statics.uploaded;
    }

    logError(err) {
        console.error(err);
    }

    // send message by socket.io  to server
    sendBySocket(event, ...args) {
        this.socket_.emit(event, ...args);
    }
}
